<style lang="scss" scoped>
* {
  box-sizing: border-box;
}
.slider {
  width: 200px;
  height: 38px;
  background-color: #1d2d3c;
  border-radius: 5px;
  position: fixed;
  z-index: 2;
  left: 50%;
  top: 25%;
  display: flex;
  align-items: center;
  padding: 0 16px;
  margin-left: -100px;
  margin-top: -19px;
  &-input {
    display: block;
    width: 100%;
    flex: 1;
    margin-right: 10px;
    background-color: transparent;
    outline: none;
    appearance: none;
    border: 0;
    text-align: center;
    color: #fff;
    font-size: 18px;

    &-unit {
      font-size: 18px;
      color: #fff;
    }
    &-wrapper {
      flex: 1;
      display: flex;
      align-items: center;
      margin: 0 10px;
      overflow: hidden;
    }
  }
  &-icon {
    width: 21px;
    height: 21px;
    background-position: center;
    background-repeat: no-repeat;
    background-size: 100%;
    cursor: pointer;
    &.minus {
      background-image: url('../images/minus.png');
    }
    &.plus {
      background-image: url('../images/plus.png');
    }
    &.close {
      width: 18px;
      height: 18px;
      position: absolute;
      right: -9px;
      top: -9px;
      background-image: url('../images/close.png');
    }
  }
}
</style>

<template>
  <div
    class="slider"
    ref="draggableElement"
    :style="{
      transform: `translate(${position.x}px, ${position.y}px)`,
      cursor: isDragging ? 'grabbing' : 'grab',
    }"
    @mousedown="startDrag"
    @mouseup="stopDrag"
    @mousemove="drag"
  >
    <div class="slider-icon minus" @click="handleMinus"></div>
    <div class="slider-input-wrapper">
      <input
        class="slider-input"
        :value="params.radius"
        @mousedown.stop
        @click.stop
        @input="handleInput"
      />
      <div class="slider-input-unit">M</div>
    </div>
    <div class="slider-icon plus" @click="handlePlus"></div>
    <div class="slider-icon close" @click="emit('close')"></div>
  </div>
  <!-- 撒点图层 -->
  <LayerMark
    v-for="(item, index) in layerConfigs"
    :key="index"
    v-bind="item"
    :params="params"
    :keepOriginal="false"
  />
</template>

<script lang="ts" setup>
import { onUnmounted, ref, inject, toRefs, watch } from 'vue'
import * as Cesium from 'cesium'
import { debounce } from 'lodash'
import { usePlacementRiskMark, usePlacementDeviceMark } from '../hooks'
import type { BillboardManager } from '../utils'
import LayerMark from './LayerMark.vue'

const maxRadius = 5000
const minRadius = 50
const step = 100

const emit = defineEmits(['close'])
const props = defineProps({
  lat: {
    type: Number,
    required: true,
  },
  lng: {
    type: Number,
    required: true,
  },
})

const billboardManager = inject<BillboardManager>('billboardManager')!
const viewer = billboardManager.viewer
const { lat, lng } = toRefs(props)
const draggableElement = ref<HTMLDivElement>()
const isDragging = ref(false)
const position = ref({ x: 0, y: 0 })
const startPos = ref({ x: 0, y: 0 })
const defaultParams = {
  queryType: 3,
  radius: 200,
  viewLimit: 200,
}
const params = ref({
  ...defaultParams,
  lat: props.lat,
  lng: props.lng,
})

const layerConfigs = Object.freeze({
  risk: {
    hook: usePlacementRiskMark,
    billboardController: billboardManager.createController('risk'),
  },
  device: {
    hook: usePlacementDeviceMark,
    billboardController: billboardManager.createController('event'),
  },
})
let circleEntity: Cesium.Entity

const handleInput = debounce((event: { target: { value: string } }) => {
  const val = event.target.value
  if (!/^\d*$/.test(event.target.value)) {
    event.target.value = minRadius.toString()
    return
  }
  const num = parseInt(val)
  let finalRadius: number
  if (isNaN(num)) {
    finalRadius = minRadius
  } else if (num < minRadius) {
    finalRadius = minRadius
  } else if (num > maxRadius) {
    finalRadius = maxRadius
  } else {
    finalRadius = num
  }
  params.value.radius = finalRadius
}, 800)

const startDrag = (e: { clientX: number; clientY: number; preventDefault: () => void }) => {
  isDragging.value = true
  startPos.value = {
    x: e.clientX - position.value.x,
    y: e.clientY - position.value.y,
  }
  e.preventDefault()

  document.addEventListener('mousemove', drag)
  document.addEventListener('mouseup', stopDrag)
}

const stopDrag = () => {
  isDragging.value = false
  document.removeEventListener('mousemove', drag)
  document.removeEventListener('mouseup', stopDrag)
}

const drag = (e: { clientX: number; clientY: number }) => {
  if (!isDragging.value) return
  position.value = {
    x: e.clientX - startPos.value.x,
    y: e.clientY - startPos.value.y,
  }
}

const handleMinus = () => {
  params.value.radius = Math.max(minRadius, params.value.radius - step)
}

const handlePlus = () => {
  params.value.radius = Math.min(maxRadius, params.value.radius + step)
}

const unmounted = () => {
  viewer.entities.remove(circleEntity)
  Object.values(layerConfigs).forEach((config) => {
    config.billboardController.clear()
  })
}

watch(
  [lat, lng],
  ([lat, lng]) => {
    unmounted()
    params.value = {
      ...defaultParams,
      lat,
      lng,
    }
    viewer.camera.flyTo({
      destination: Cesium.Cartesian3.fromDegrees(lng, lat, 1500),
      complete: () => {
        // 画圈
        circleEntity = viewer.entities.add({
          name: '雷达圈',
          position: Cesium.Cartesian3.fromDegrees(lng, lat),
          ellipse: {
            height: 0,
            semiMinorAxis: new Cesium.CallbackProperty(() => params.value.radius, false),
            semiMajorAxis: new Cesium.CallbackProperty(() => params.value.radius, false),
            material: Cesium.Color.fromCssColorString('#00BFFF').withAlpha(0.3),
            outline: true,
            outlineColor: Cesium.Color.fromCssColorString('#00BFFF').withAlpha(1),
          },
        })
      },
    })
  },
  { immediate: true }
)

onUnmounted(() => {
  unmounted()
})
</script>
